package dq.study.jvm.classloader.instance;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * classloader中的loadClass与findClass的区别
 *
 * @author dongqun
 * @version 1.0.0
 * @since 2020/12/31 13:48:00
 */
public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //重写findclass方法的classloader
        OverrideFindClassMethodClassLoader classLoader = new OverrideFindClassMethodClassLoader();
        Class testAClass = classLoader.findClass("dq.study.jvm.classloader.instance.TestA");
        Method main = testAClass.getDeclaredMethod("main",String[].class);
        //期待输出：
        // TestA:dq.study.jvm.classloader.instance.OverrideFindClassMethodClassLoader@74a14482
        // TestB:dq.study.jvm.classloader.instance.OverrideFindClassMethodClassLoader@74a14482
        //实际输出：
        //TestA:dq.study.jvm.classloader.instance.OverrideFindClassMethodClassLoader@74a14482
        //TestB:sun.misc.Launcher$AppClassLoader@18b4aac2
        //原因分析：
        //重写findclass方法的类加载器，加载TestA类时使用OverrideFindClassMethodClassLoader，但是加载TestB时因为classloader的双亲委派原则，
        //它默认会优先用父级的classloader加载class，所以看到的TestB加载用的是AppClassLoader
        main.invoke(null,new Object[]{args});


        //重写loadclass方法的classloader
        //输出：
        //  TestA:dq.study.jvm.classloader.instance.OverrideLoadClassMethodClassLoader@74a14482
        //  TestB:dq.study.jvm.classloader.instance.OverrideLoadClassMethodClassLoader@74a14482
        //分析：
        // 因为重写了loadclass方法所以就自定义了类加载的流程，不会走双亲委派的机制，
        // 这样的话java.lang包中的类也会走这个加载器，为了处理这部分类，重写loadclass方法的加载器的构造方法传入了一个程序当前类加载器的父加载器，委托父加载器加载java.lang包中的类
        OverrideLoadClassMethodClassLoader overrideLoadClassMethodClassLoader = new OverrideLoadClassMethodClassLoader(Thread.currentThread().getContextClassLoader().getParent());
        Class testAClass2 = overrideLoadClassMethodClassLoader.loadClass("dq.study.jvm.classloader.instance.TestA");
        Method main2 = testAClass2.getDeclaredMethod("main",String[].class);
        main2.invoke(null,new Object[]{args});


    }
}
